﻿using Actor.Net.Network.Gate.Adapter;
using Actor.Net.Network.Gate.Tcp;
using Actor.Net.Network.Gate.WebSocket;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace Actor.Net.Network.Gate
{
    /// <summary>
    /// 序列化库类型
    /// </summary>
    public enum SerializationType
    {
        /// <summary>
        /// Protobuf
        /// </summary>
        Protobuf = 0,
        /// <summary>
        /// MessagePack
        /// </summary>
        MessagePack = 1,
    }

    /// <summary>
    /// 网络库类
    /// </summary>
    public class GateNetwork : SynchronizationContext, IDisposable
    {
        /// <summary>
        /// 序列化库类型
        /// </summary>
        public SerializationType SerializationType { get; }
        /// <summary>
        /// Network Id
        /// </summary>
        public int Id { get; }
        /// <summary>
        /// Socket类型(TCP,KCP,WebScoket)
        /// </summary>
        public GateNetType NetType { get; }
        /// <summary>
        /// 本地Socket监听IP端口或者是远程Socket服务监听IP端口
        /// </summary>
        public IPEndPoint ConnectEndPoint { get; }
        /// <summary>
        /// WebSocket链接前缀
        /// </summary>
        public string HttpPrefixed { get; }
        /// <summary>
        /// 是否自动重连
        /// </summary>
        public bool AutoReconnecting { get; set; }
        /// <summary>
        /// 连接超时时长定义，默认30000毫秒(30秒)
        /// 如果服务在30秒之内没有收到任何消息，客户端发送一个Ping包到服务端测试连接是否断开。
        /// </summary>
        public long ConnectionTimeoutMilliseconds { get; set; }
        /// <summary>
        /// 连接掉线时长定义，默认180000毫秒(3分钟)，
        /// 服务检测在3分钟之内没有收到客户端任何消息，当客户端已经断开连接处理，
        /// </summary>
        public long ConnectionDroppedMilliseconds { get; set; }
        /// <summary>
        /// 是否关闭超时没有通讯Socket
        /// </summary>
        public bool IsIsDropTimeout { get; set; }
        /// <summary>
        /// Socket执行上下文队列
        /// </summary>
        private static ConcurrentQueue<Action> SynchronizationContextQueue { get; } = new ConcurrentQueue<Action>();
        /// <summary>
        /// 网络服务
        /// </summary>
        public GateBaseService NetService { get; private set; }
        /// <summary>
        /// 客户端Socket连接上下文
        /// </summary>
        public GateChannelContext ClienContext => this.NetService.ConnectChannel.ChannelContext;
        /// <summary>
        /// 工作线程
        /// </summary>
        public static int WorkThreadId { get; set; }

        private Action<GateChannelContext> OnConnected { get; set; }
        private Action<GateChannelContext> OnDisconnected { get; set; }
        private Action<GateChannelContext, Exception> OnChannelError { get; set; }
        private Action<GateChannelContext> OnReconnectFailed { get; set; }
        private ConcurrentDictionary<int, IGateMessageAdapter> HandlerAdapterMappingCache { get; } = new ConcurrentDictionary<int, IGateMessageAdapter>();
        private Dictionary<int, Type> HandlerAdapterTypeMappingCache { get; } = new Dictionary<int, Type>();
        private Dictionary<int, Type> MessageTypeMappingCache { get; } = new Dictionary<int, Type>();
        private static ConcurrentDictionary<int, GateNetwork> gameNetworks { get; } = new ConcurrentDictionary<int, GateNetwork>();

        /// <summary>
        /// 无参构造
        /// </summary>
        private GateNetwork() { }

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="netType">Socket类型</param>
        /// <param name="port">本地Socket监听端口或者是远程Socket服务监听端口</param>
        /// <param name="serialization">序列化库类型</param>
        public GateNetwork(GateNetType netType, int port, SerializationType serialization = SerializationType.MessagePack)
            : this(netType, new IPEndPoint(IPAddress.Parse("0.0.0.0"), port), serialization)
        {
        }

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="netType">Socket类型</param>
        /// <param name="ipAddress">本地Socket监听IP或者是远程Socket服务监听IP</param>
        /// <param name="port">本地Socket监听端口或者是远程Socket服务监听端口</param>
        /// <param name="serialization">序列化库类型</param>
        public GateNetwork(GateNetType netType, string ipAddress, int port, SerializationType serialization = SerializationType.MessagePack)
            :this(netType, new IPEndPoint(IPAddress.Parse(ipAddress), port), serialization)
        {
        }

        /// <summary>
        /// 构造函数(WebSocket)
        /// </summary>
        /// <param name="httpPrefixed">本地Socket监听IP或者是远程Socket服务监听IP</param>
        public GateNetwork(string httpPrefixed)
        {
            this.SerializationType = SerializationType.MessagePack;
            this.Id = GateNetworkIdCreator.CreateId();
            this.NetType = GateNetType.WebSocket;
            this.HttpPrefixed = httpPrefixed;
            this.AutoReconnecting = true;
            this.ConnectionTimeoutMilliseconds = 30 * 1000;
            this.ConnectionDroppedMilliseconds = 60 * 1000 * 3;
            //this.Bind(this.GetType().Assembly);
            this.IsIsDropTimeout = true;
            this.NetService = new GateWebSocketService(this);
            gameNetworks.TryAdd(this.Id, this);
        }

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="netType">Socket类型</param>
        /// <param name="connectEndpoint">本地Socket监听IP端口或者是远程Socket服务监听IP端口</param>
        /// <param name="serialization">序列化库类型</param>
        public GateNetwork(GateNetType netType, IPEndPoint connectEndpoint, SerializationType serialization = SerializationType.Protobuf)
        {
            this.SerializationType = serialization;
            this.Id = GateNetworkIdCreator.CreateId();
            this.NetType = netType;
            this.ConnectEndPoint = connectEndpoint;
            this.AutoReconnecting = true;
            this.ConnectionTimeoutMilliseconds = 30 * 1000;
            this.ConnectionDroppedMilliseconds = 60 * 1000 * 3;
            //this.Bind(this.GetType().Assembly);
            this.IsIsDropTimeout = true;

            if (this.NetType == GateNetType.Tcp)
            {
                this.NetService = new GateTcpService(this);
            }
            else if (this.NetType == GateNetType.WebSocket)
            {
                this.NetService = new GateWebSocketService(this);
            }
            gameNetworks.TryAdd(this.Id, this);
        }

        /// <summary>
        /// 绑定消息适配器消息
        /// </summary>
        /// <typeparam name="TAdapter"></typeparam>
        /// <param name="command"></param>
        public void Bind<TAdapter>(ushort command) where TAdapter : IGateMessageAdapter, new()
        {
            Bind(command, typeof(TAdapter));
        }

        /// <summary>
        /// 绑定消息适配器消息
        /// </summary>
        /// <typeparam name="TAdapter"></typeparam>
        /// <param name="command"></param>
        public void Bind<TAdapter>(Enum command) where TAdapter : IGateMessageAdapter, new()
        {
            var id = Convert.ToUInt16(command);
            Bind<TAdapter>(id);
        }

        /// <summary>
        /// 绑定消息适配器消息
        /// </summary>
        /// <param name="assembly"></param>
        public void Bind(Assembly assembly)
        {
            var types = assembly.GetTypes().Where(t => typeof(IGateMessageAdapter).IsAssignableFrom(t));
            if (!types.Any())
                return;

            foreach (var type in types)
            {
                var attributes = type.GetCustomAttributes<GateAdapterBinderAttribute>();
                if (attributes == null || !attributes.Any())
                    continue;

                foreach (var attribute in attributes)
                    Bind(attribute.Command, type);
            }
        }

        /// <summary>
        /// 绑定消息适配器消息
        /// </summary>
        /// <param name="command"></param>
        /// <param name="adapterType"></param>
        public void Bind(int command, Type adapterType)
        {
            if (HandlerAdapterMappingCache.ContainsKey(command))
                return;

            HandlerAdapterTypeMappingCache[command] = adapterType;
            var messageType = adapterType.BaseType.GetGenericArguments().FirstOrDefault();
            MessageTypeMappingCache[command] = messageType;
        }

        /// <summary>
        /// 获取一个消息适配器
        /// </summary>
        /// <param name="command"></param>
        /// <returns></returns>
        internal IGateMessageAdapter GetAdapter(int command)
        {
            if (HandlerAdapterMappingCache.TryGetValue(command, out IGateMessageAdapter adapter))
                return adapter;

            if (!HandlerAdapterTypeMappingCache.TryGetValue(command, out Type adapterType))
                throw new Exception($"Command {command} Not bound a adapter type.");

            adapter = (IGateMessageAdapter)Activator.CreateInstance(adapterType);
            HandlerAdapterMappingCache.AddOrUpdate(command, adapter, (k, v) => adapter);
            return adapter;
        }

        /// <summary>
        /// 获取一个消息类型
        /// </summary>
        /// <param name="command"></param>
        /// <returns></returns>
        internal Type GetMessageType(int command)
        {
            MessageTypeMappingCache.TryGetValue(command, out Type type);
            return type;
        }

        /// <summary>
        /// 添加连接事件处理者
        /// </summary>
        /// <param name="handler"></param>
        public void AddConnectedHandler(Action<GateChannelContext> handler)
        {
            this.OnConnected += handler;
            this.NetService.OnConnected = this.OnConnected;
        }

        /// <summary>
        /// 添加连接断开处理者
        /// </summary>
        /// <param name="handler"></param>
        public void AddDisconnectedHandler(Action<GateChannelContext> handler)
        {
            this.OnDisconnected += handler;
            this.NetService.OnDisconnected = this.OnDisconnected;
        }

        /// <summary>
        /// 添加重连失败处理者
        /// </summary>
        /// <param name="handler"></param>
        public void AddReconnectFailedHandler(Action<GateChannelContext> handler)
        {
            this.OnReconnectFailed += handler;
            this.NetService.OnReconnectFailed = this.OnReconnectFailed;

        }

        /// <summary>
        /// 添加Socket异常处理者
        /// </summary>
        /// <param name="handler"></param>
        public void AddSocketErrorHandler(Action<GateChannelContext, Exception> handler)
        {
            this.OnChannelError += handler;
            this.NetService.OnChannelError = this.OnChannelError;
        }

        /// <summary>
        /// 执行上下文处理调用
        /// </summary>
        /// <param name="obj"></param>
        public void ProcessContextAction(object obj)
        {
            var action = obj as Action;
            action();
        }

        /// <summary>
        /// 连接服务端
        /// </summary>
        /// <param name="timeoutMilliSecond">连接超时时间</param>
        /// <returns></returns>
        public void Connect(int timeoutMilliSecond = 1000 * 8)
        {
            this.NetService.ServiceType = NetServiceType.Client;
            _ = this.NetService.Connect(timeoutMilliSecond);
        }

        /// <summary>
        /// 连接服务端
        /// </summary>
        /// <param name="cancellationToken">连接超时时间</param>
        /// <returns></returns>
        public async Task ConnectAsync(CancellationToken cancellationToken)
        {
            this.NetService.ServiceType = NetServiceType.Client;
            _ = await this.NetService.ConnectAsync(cancellationToken);
        }

        /// <summary>
        /// 监听Socket端口
        /// </summary>
        public void Listen()
        {
            this.NetService.ServiceType = NetServiceType.Server;
            this.NetService.Listen();
            Start();
        }

        /// <summary>
        /// 获取所有连接上下文
        /// </summary>
        /// <returns></returns>
        public List<GateChannelContext> GetContextList()
        {
            return GateBaseService.ChannelContexts.Values.Where((ctx) => ctx.NetworkId == this.Id).OrderBy((ctx) => ctx.ChannelId).ToList();
        }

        /// <summary>
        /// 添加Socket事件到执行上下文队列
        /// </summary>
        /// <param name="callback"></param>
        /// <param name="state"></param>
        public override void Post(SendOrPostCallback callback, object state)
        {
            if (WorkThreadId == Thread.CurrentThread.ManagedThreadId)
            {
                try
                {
                    callback(state);
                }
                catch(Exception ex)
                {
                    this.OnChannelError?.Invoke(null, ex);
                }
                return;
            }
            SynchronizationContextQueue.Enqueue(() => { callback(state); });
        }

        /// <summary>
        /// 更新所有Socket事件
        /// </summary>
        public static void Start()
        {
            Task.Run(() =>
            {
                WorkThreadId = Thread.CurrentThread.ManagedThreadId;
                while (true)
                {
                    foreach (var network in gameNetworks.Values)
                    {
                        network.UpdateSynchronizationContext();
                    }
                    Thread.Sleep(1);
                }
            });
        }

        /// <summary>
        /// 执行Socket事件上下文
        /// </summary>
        private void UpdateSynchronizationContext()
        {
            try
            {
                this.NetService?.Update();
            }
            catch (Exception ex)
            {
                this.OnChannelError?.Invoke(null, ex);
            }

            while (true)
            {
                if (!SynchronizationContextQueue.TryDequeue(out Action action))
                    return;

                try
                {
                    action();
                }
                catch(Exception ex)
                {
                    this.OnChannelError?.Invoke(null, ex);
                }
            }
        }

        /// <summary>
        /// 释放对象
        /// </summary>
        public void Dispose()
        {
            if(gameNetworks.TryRemove(this.Id, out GateNetwork network))
            {
                network.NetService.Dispose();
            }
        }
    }
}
